/********************************************************************************
* Project       : No Latency RTSP streaming Project
* Author        : Seo, SeokWon
* e-mail        : kueony@gmail.com
* Licence       : See the Copyright
*********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/poll.h>
#include <errno.h>

#include "lib_network.h"

typedef struct pollfd   st_PollFd;

int MakeSocketAndConnect(char *a_pcAddr, int a_iPort)
{
    int 				iSock = 0;
    struct hostent      *pstHostEnt = NULL;
    struct sockaddr_in  stServAddr;     /* connector's address information */
	unsigned long		ulS_Addr = 0;

	bzero(&stServAddr, sizeof(stServAddr));

    if((iSock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        return -1;
    }

	if((ulS_Addr = inet_addr(a_pcAddr)) == INADDR_NONE)
	{
    	if((pstHostEnt = gethostbyname(a_pcAddr)) == NULL)
	    {
			close(iSock);
        	return -1;
	    }

		ulS_Addr = (unsigned long) *((unsigned long *) pstHostEnt->h_addr_list[0]);
	}

    stServAddr.sin_family		= AF_INET;
    stServAddr.sin_port			= htons(a_iPort);
    stServAddr.sin_addr.s_addr	= ulS_Addr;

    if(connect(iSock,(struct sockaddr *)&stServAddr, sizeof(struct sockaddr)) < 0)
    {
		close(iSock);
        return -1;
    }

	return iSock;	
}


int MakeSocketAndConnectUDP(char* a_pcAddr, int a_iPort)
{
    int 				iSock = 0;
    struct hostent      *pstHostEnt = NULL;
    struct sockaddr_in  stServAddr;     /* connector's address information */
	unsigned long		ulS_Addr = 0;

	bzero(&stServAddr, sizeof(stServAddr));

    if((iSock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        return -1;
    }

	if((ulS_Addr = inet_addr(a_pcAddr)) == INADDR_NONE)
	{
    	if((pstHostEnt = gethostbyname(a_pcAddr)) == NULL)
	    {
			close(iSock);
        	return -1;
	    }

		ulS_Addr = (unsigned long) *((unsigned long *) pstHostEnt->h_addr_list[0]);
	}

    stServAddr.sin_family		= AF_INET;
    stServAddr.sin_port			= htons(a_iPort);
    stServAddr.sin_addr.s_addr	= ulS_Addr;

    if(connect(iSock,(struct sockaddr *)&stServAddr, sizeof(struct sockaddr)) < 0)
    {
		close(iSock);
        return -1;
    }

	return iSock;	
}


int MakeMulticastSocket(unsigned long a_ulAddr, int a_iPort)
{
	int					iSock = 0;
	struct sockaddr_in	stAddr;
    struct ip_mreq		stJoinAddr;
	int					iOpt = 1;

    if((iSock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		return -1;
	}

    bzero(&stAddr, sizeof(stAddr));

    stAddr.sin_family		= AF_INET;
    stAddr.sin_addr.s_addr	= htonl(INADDR_ANY);
    stAddr.sin_port   		= a_iPort;

	
    setsockopt(iSock, SOL_SOCKET, SO_REUSEADDR, &iOpt, sizeof(iOpt));

    if(bind(iSock, (struct sockaddr *)&stAddr, sizeof(stAddr)) < 0)
    {
		close(iSock);
		return -1;
    }

//    stJoinAddr.imr_multiaddr.s_addr = inet_addr(a_pcIp);
    stJoinAddr.imr_multiaddr.s_addr = a_ulAddr;
    stJoinAddr.imr_interface.s_addr = htonl(INADDR_ANY);

    if(setsockopt(iSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *) &stJoinAddr, sizeof(stJoinAddr)) < 0)
	{
		close(iSock);
		return -1;
	}

	return iSock;
}


int MakeServerSocket(char* a_pcAddr, int a_iPort)
{
	int 				iOpt;
	int 				iOptLen;
	int					iSock;
    struct hostent      *pstHostEnt = NULL;
    struct sockaddr_in  stServAddr; 
	unsigned long		ulS_Addr = 0;

	bzero(&stServAddr, sizeof(stServAddr));

    if((iSock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        return -1;
    }

	if((ulS_Addr = inet_addr(a_pcAddr)) == INADDR_NONE)
	{
    	if((pstHostEnt = gethostbyname(a_pcAddr)) == NULL)
	    {
			close(iSock);
        	return -1;
	    }

		ulS_Addr = (unsigned long) *((unsigned long *) pstHostEnt->h_addr_list[0]);
	}

    stServAddr.sin_family		= AF_INET;
    stServAddr.sin_port			= htons(a_iPort);
    stServAddr.sin_addr.s_addr	= ulS_Addr;
	
	iOpt		= 1;
	iOptLen	= sizeof(iOpt);
	
	setsockopt(iSock, SOL_SOCKET, SO_REUSEADDR, &iOpt, iOptLen);

	if(bind(iSock, (struct sockaddr *) &stServAddr, sizeof(stServAddr)) < 0)
	{
		perror("bind");
		close(iSock);
		return -1;
	}
	
	if(listen(iSock, 5) < 0)
	{
		perror("listen");
		close(iSock);
		return -1;
	}
	
	return iSock;
}


int MakeServerSocketUDP(char* a_pcAddr, int a_iPort)
{
	int 				iOpt;
	int 				iOptLen;
	int					iSock;
    struct hostent      *pstHostEnt = NULL;
    struct sockaddr_in  stServAddr; 
	unsigned long		ulS_Addr = 0;

	bzero(&stServAddr, sizeof(stServAddr));

    if((iSock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        return -1;
    }

	if((ulS_Addr = inet_addr(a_pcAddr)) == INADDR_NONE)
	{
    	if((pstHostEnt = gethostbyname(a_pcAddr)) == NULL)
	    {
			close(iSock);
        	return -1;
	    }

		ulS_Addr = (unsigned long) *((unsigned long *) pstHostEnt->h_addr_list[0]);
	}

    stServAddr.sin_family		= AF_INET;
    stServAddr.sin_port			= htons(a_iPort);
    stServAddr.sin_addr.s_addr	= ulS_Addr;
	
	iOpt		= 1;
	iOptLen	= sizeof(iOpt);
	
//	setsockopt(iSock, SOL_SOCKET, SO_REUSEADDR, &iOpt, iOptLen);

	if(bind(iSock, (struct sockaddr *) &stServAddr, sizeof(stServAddr)) < 0)
	{
		perror("bind");
		close(iSock);
		return -1;
	}
	
	return iSock;
}


int SendMsg4FixSize(int a_iSock, char* a_pcData, int a_iSize)
{
	int		iLeftSize;
	int		iSendSize;
	char*	pcData;

	iLeftSize = a_iSize;
	pcData	= a_pcData;
	
	while(iLeftSize > 0)
	{
		errno = 0;
		iSendSize = write(a_iSock, pcData, iLeftSize);

		if(iSendSize <= 0)
		{
			if(errno == EINTR)
			{
				iSendSize = 0;
			}
			else
			{
				return -1;
			}
		}
		
		pcData	+= iSendSize;
		iLeftSize	-= iSendSize;
	}
	
	return 0;
}


int RecvMsg4FixSize(int a_iSock, char* a_pcData, int a_iSize)
{
	int		iLeftSize;
	int		iRecvSize;
	char*	pcData;

	iLeftSize = a_iSize;
	pcData	= a_pcData;

	while(iLeftSize > 0)
	{
		errno = 0;
		iRecvSize = read(a_iSock, pcData, iLeftSize);

        if(iRecvSize <= 0)
		{
			if(errno == EINTR)
			{
				iRecvSize = 0;
			}
			else
			{
				return -1;
			}
		}
		
		pcData	+= iRecvSize;
		iLeftSize	-= iRecvSize;
	}
	
	return 0;
}



int SendMsg4FixSizeTimeOut(int a_iSock, char *a_pcData, int a_iSize, unsigned int a_uiMillSec)
{
	st_PollFd	stPoll;
	int			iResult;
	int			iLeftSize;
	int			iSendSize;
	char*		pcData;

	iLeftSize = a_iSize;
	pcData	= a_pcData;

	memset(&stPoll, 0, sizeof(stPoll));

	stPoll.fd		= a_iSock;
	stPoll.events	= POLLOUT;

	while(iLeftSize > 0)
	{
		errno = 0;
		if((iResult = poll(&stPoll, 1, a_uiMillSec)) < 0)
		{
			if(errno != EINTR)
			{
				return -1;
			}
		}
		else if(iResult == 0)
		{
			return -2;
		}
		else
		{
			if(stPoll.revents & POLLOUT)
			{
				errno = 0;
				iSendSize = write(a_iSock, pcData, iLeftSize);

				if(iSendSize <= 0)
				{
					if(errno == EINTR)
					{
						iSendSize = 0;
					}
					else
					{
						return -1;
					}
				}
		
				pcData	+= iSendSize;
				iLeftSize	-= iSendSize;
			}
			else
			{
				return -1;
			}
		}
	}

	return 0;
}


int RecvMsg4FixSizeTimeOut(int a_iSock, char *a_pcData, int a_iSize, unsigned int a_uiMillSec)
{
	st_PollFd	stPoll;
	int			iResult;
	int			iLeftSize;
	int			iRecvSize;
	char*		pcData;

	iLeftSize = a_iSize;
	pcData	= a_pcData;

	memset(&stPoll, 0, sizeof(stPoll));

	stPoll.fd		= a_iSock;
	stPoll.events	= POLLIN;

	while(iLeftSize > 0)
	{
		errno = 0;
		if((iResult = poll(&stPoll, 1, a_uiMillSec)) < 0)
		{
			if(errno != EINTR)
			{
				return -1;
			}
		}
		else if(iResult == 0)
		{
			return -2;
		}
		else
		{
			if(stPoll.revents & POLLIN)
			{
				errno = 0;
				iRecvSize = read(a_iSock, pcData, iLeftSize);

		        if(iRecvSize <= 0)
				{
					if(errno == EINTR)
					{
						iRecvSize = 0;
					}
					else
					{
						return -1;
					}
				}
		
				pcData	+= iRecvSize;
				iLeftSize	-= iRecvSize;
				
			}
			else
			{
				return -1;
			}
		}
	}

	return 0;
}


int RequestHttpPage(char *a_pcAddr, char *a_pcPage, int a_iPort)
{
    int 				iSock = 0;
    struct hostent      *pstHostEnt = NULL;
    struct sockaddr_in  stServAddr;     /* connector's address information */
	unsigned long		ulS_Addr = 0;

	memset(&stServAddr, 0, sizeof(stServAddr));

    if((iSock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        return -1;
    }

	if((ulS_Addr = inet_addr(a_pcAddr)) == INADDR_NONE)
	{
    	if((pstHostEnt = gethostbyname(a_pcAddr)) == NULL)
	    {
			close(iSock);
        	return -1;
	    }

		ulS_Addr = (unsigned long) *((unsigned long *) pstHostEnt->h_addr_list[0]);
	}

    stServAddr.sin_family		= AF_INET;      /* host byte order */
    stServAddr.sin_port			= htons(a_iPort);      /* short, network byte order */
    stServAddr.sin_addr.s_addr	= ulS_Addr;

    if(connect(iSock,(struct sockaddr *)&stServAddr, sizeof(struct sockaddr)) < 0)
    {
		close(iSock);
        return -1;
    }

    char caHttpMsg[1024];

    memset (caHttpMsg, 0x00, sizeof(caHttpMsg));
    sprintf(caHttpMsg, "GET /%s HTTP/1.0\r\n", a_pcPage);
    SendMsg4FixSize(iSock, caHttpMsg, strlen(caHttpMsg));

    memset (caHttpMsg, 0x00, sizeof(caHttpMsg));     sprintf(caHttpMsg, "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*\r\n");
    SendMsg4FixSize(iSock, caHttpMsg, strlen(caHttpMsg));

    memset (caHttpMsg, 0x00, sizeof(caHttpMsg));
    sprintf(caHttpMsg, "Accept-Language: ko\r\n");
    SendMsg4FixSize(iSock, caHttpMsg, strlen(caHttpMsg));

	memset (caHttpMsg, 0x00, sizeof(caHttpMsg));
    sprintf(caHttpMsg, "Accept-Encoding: gzip, deflate\r\n");
    SendMsg4FixSize(iSock, caHttpMsg, strlen(caHttpMsg));

    memset (caHttpMsg, 0x00, sizeof(caHttpMsg));
    sprintf(caHttpMsg, "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1; .NET CLR 1.1.4322)\r\n");
    SendMsg4FixSize(iSock, caHttpMsg, strlen(caHttpMsg));

    memset (caHttpMsg, 0x00, sizeof(caHttpMsg));
    sprintf(caHttpMsg, "Host: %s\r\n", a_pcAddr);
    SendMsg4FixSize(iSock, caHttpMsg, strlen(caHttpMsg));

    memset (caHttpMsg, 0x00, sizeof(caHttpMsg));
    sprintf(caHttpMsg, "Connection: close\r\n\r\n");
    SendMsg4FixSize(iSock, caHttpMsg, strlen(caHttpMsg));

    return iSock;
}


int RequestHttpData(char *a_pcAddr, char *a_pcPage, int a_iPort, unsigned int* a_puiSize)
{
	int		iSock;
	char	caBuf[512] = {0, };
	int		iCheck = 0;
	int		i;

	if((iSock = RequestHttpPage(a_pcAddr, a_pcPage, a_iPort)) < 0)
	{
		return -1;
	}

	for(i = 0 ; i < (int) sizeof(caBuf) ; i++)
	{
		if(read(iSock, caBuf + i, 1) <= 0)
		{
			return -1;
		}

		if(strstr(caBuf, "\r\n\r\n") != NULL) 
		{
			iCheck = 1;
			break;
		}
	}

	if(iCheck == 0) 
	{
		close(iSock);
		return -1;
	}

	if(strstr(caBuf, "HTTP/1.1 200 OK") == NULL)
	{
		return -1;
	}

	char*	pcStr;
	char	caSize[512] = {0, };

	if((pcStr = strstr(caBuf, "Content-Length:")) == NULL)
	{
		close(iSock);
		return -1;
	}

	pcStr = pcStr + 15;

	for(i = 0 ; i < (int) strlen(pcStr) ; i++)
	{
		if(pcStr[i] == '\n') break;
		caSize[i] = pcStr[i];
	}

	*a_puiSize = strtoul(caSize, NULL, 10);

	return iSock;
}


int SetSockTime(int a_iSock, int a_iSec)
{
	int		iCheckSend = 1;
	int		iCheckRecv = 1;
    struct timeval  stTime;

    stTime.tv_sec     = a_iSec;
    stTime.tv_usec    = 0;

    if(setsockopt(a_iSock, SOL_SOCKET, SO_SNDTIMEO, &stTime, sizeof(stTime)) < 0)
    {
		iCheckSend = 0;
    }

    if(setsockopt(a_iSock, SOL_SOCKET, SO_RCVTIMEO, &stTime, sizeof(stTime)) < 0)
    {
		iCheckRecv = 0;
    }

	if(iCheckSend == 0 || iCheckRecv == 0) return -1;

    return 0;
}

